/**
 * @author        Nicolas Bottarini <nicolasbottarini@gmail.com>, 404 <http://www.proyecto404.com>
 * @version       $Rev: 3 $
 * @lastrevision  $Date: 2011-09-12 16:45:37 +0000 (Mon, 12 Sep 2011) $	 
 */
package com.proyecto404.core.ui {
	import com.proyecto404.core.IBaseObject;
	import com.proyecto404.core.collections.Dictionary;
	import com.proyecto404.core.collections.IIterator;
	import com.proyecto404.core.collections.KeyValuePair;
	import com.proyecto404.core.exception.InvalidOperationException;
	import com.proyecto404.core.ui.util.DisplayObjectHelper;
	import com.proyecto404.core.util.ClassHelper;
	
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.FrameLabel;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	import org.osflash.signals.natives.INativeDispatcher;
	import org.osflash.signals.natives.NativeSignal;
	
	public class BaseMovieClip extends flash.display.MovieClip implements IBaseObject {
		private var _labels:Dictionary;
		private var _click:NativeSignal;
		private var _registeredFrameFunctions:Dictionary;

		public function BaseMovieClip() {
			super();
			
			_click = new NativeSignal(this, 'click', MouseEvent);
			
			_labels = new Dictionary();
			_registeredFrameFunctions = new Dictionary();
			
			for each (var label:FrameLabel in currentLabels) {
				_labels.add(label.name, label);
			}
		}
		
		protected function get labels():Dictionary {
			return _labels;
		}
		
		public function rewind(callback:Function = null):void {
			clearAllFrameFunctions();
			addEventListener(Event.ENTER_FRAME, function (e:Event):void {
				if(currentFrame > 1) {
					gotoAndStop(currentFrame - 1);
				} else {
					if (callback != null) {
						callback.call();
						callback = null;
					}
				}
			});
		}
		
		public function setProps(o:Object,props:Object):* {
			for (var n:String in props) {
				o[n] = props[n];
			}
			return o;
		}
		
		public function drawRect(posX:Number = 0,posY:Number = 0,sizeX:Number = 100,sizeY:Number = 100,color:Number = 0xFF0000,opacity:Number = 1):void {
			var rect:Sprite;
			rect = new Sprite();
			rect.graphics.beginFill(color,alpha);
			rect.graphics.drawRect(posX,posY,sizeX,sizeY);
			rect.graphics.endFill();
			addChild(rect);
		}
		protected function getLabelFrame(label:String):int {
			return FrameLabel(labels.get(label)).frame;
		}
		
		protected function getLabelLastFrame(label:String):int {
			var frame:int = FrameLabel(labels.get(label)).frame;
			var labelsIt:IIterator = labels.getValues().getIterator();
			var currentFrame:int = totalFrames + 1;
			while(labelsIt.hasNext()) {
				var labelFrame:int = FrameLabel(labelsIt.next()).frame;
				if (labelFrame < currentFrame && labelFrame > frame) {
					currentFrame = labelFrame;
				} 		
			}
			return currentFrame - 1;
		}

		public function get click():NativeSignal {
			return _click;
		}
		
		public function registerFrameFunction(frameNumber:int, func:Function):void {
			_registeredFrameFunctions.add((frameNumber - 1).toString(), func);
			addFrameScript(frameNumber - 1, func);
		}
		
		public function registerLabelFunction(label:String, func:Function):void {
			registerFrameFunction(FrameLabel(labels.get(label)).frame, func);
		}
		
		public function clearFrameFunction(frameNumber:int):void {
			var key:String = (frameNumber - 1).toString();
			if (!_registeredFrameFunctions.containsKey(key)) {
				return;
			}
			//addFrameScript(frameNumber - 1, _registeredFrameFunctions.get(key), false, false);
			addFrameScript(frameNumber - 1, null);
			_registeredFrameFunctions.removeKey(key);
		}
		
		public function clearAllFrameFunctions():void {
			for(var i:int = 1; i <= totalFrames;i++) {
				clearFrameFunction(i);
			}
		}

		/**
		 * Test for equality.
		 * Method Should be overriden
		 */
		public function equals(value:*):Boolean {
			if (!(value is BaseMovieClip)) {
				return false;
			}
			
			return value == this;
		}
		
		public override function toString():String {
			return getClassName();
		}
		
		public function getClass():Class {
			return ClassHelper.getClass(this);
		}
		
		public function getClassName():String {
			return ClassHelper.getClassName(this);
		}
		
		public function enableMouseInput():void {
			mouseChildren = true;
			mouseEnabled = true;
		}

		public function disableMouseInput():void {
			mouseChildren = false;
			mouseEnabled = false;
		}
		
		public function addChildPreservingPosition(child:DisplayObject):void {
			var childGlobalPos:Point = child.localToGlobal(new Point());
			var childLocalPos:Point = globalToLocal(childGlobalPos);
			child.x = childLocalPos.x;
			child.y = childLocalPos.y;
			addChild(child);
		} 

		public function addChildAtPreservingPosition(child:DisplayObject, index:int):void {
			var childGlobalPos:Point = child.localToGlobal(new Point());
			var childLocalPos:Point = globalToLocal(childGlobalPos);
			child.x = childLocalPos.x;
			child.y = childLocalPos.y;
			addChildAt(child, index);
		} 
		
		public function removeAllChilds():void {
			var i:int = numChildren;
			while(i--) {
				removeChildAt(i);
			}
		}
		
		protected function makeClickeable(mc:MovieClip = null):void {
			if (mc == null) {
				mc = this;
			}
			mc.buttonMode = true;
			mc.mouseChildren = false;
		}
		
		protected function resetClickeable(mc:MovieClip = null):void {
			if (mc == null) {
				mc = this;
			}
			mc.buttonMode = false;
			mc.mouseChildren = false;
		}
		
		/**
		 * Returns the distance from the registration point of the specified 
		 * object to the bottom-most visible pixel, ignoring any region
		 * that is not visible due to masking. For example, if a display
		 * object contains a 100-pixel-high shape and a 50-pixel-high mask,
		 * getVisibleHeight() will return 50, whereas DisplayObject's
		 * "height" variable would yield 100. 
		 * 
		 * The maximum measureable dimensions of the supplied object is
		 * 2000x2000.
		 */
		public function getVisibleHeight ():Number {
			var bitmapDataSize:int = 2000;
			var bounds:Rectangle;
			var bitmapData:BitmapData = new BitmapData(bitmapDataSize, bitmapDataSize, true, 0);
			bitmapData.draw(this);
			bounds = bitmapData.getColorBoundsRect( 0xFF000000, 0x00000000, false );
			bitmapData.dispose(); 
			return bounds.y + bounds.height;
		}
		/**
		 * Returns the distance from the registration point of the specified 
		 * object to the right-most visible pixel, ignoring any region
		 * that is not visible due to masking. For example, if a display
		 * object contains a 100-pixel-wide shape and a 50-pixel-wide mask,
		 * getVisibleWidth() will return 50, whereas DisplayObject's
		 * "width" variable would yield 100. 
		 * 
		 * The maximum measureable dimensions of the supplied object is
		 * 2000x2000.
		 */
		public function getVisibleWidth():Number {
			var bitmapDataSize:int = 2000;
			var bounds:Rectangle;
			var bitmapData:BitmapData = new BitmapData(bitmapDataSize, bitmapDataSize, true, 0);
			bitmapData.draw(this);
			bounds = bitmapData.getColorBoundsRect( 0xFF000000, 0x00000000, false );
			bitmapData.dispose(); 
			return bounds.x + bounds.width;
		}
	}
}